<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>EditContext API Demo</title>
    <style>
      body {
        font-family: Arial, sans-serif;
        max-width: 800px;
        margin: 0 auto;
        padding: 20px;
      }
      .editor {
        border: 1px solid #ccc;
        padding: 10px;
        min-height: 100px;
        margin-bottom: 20px;
      }
      .status {
        border: 1px solid #eee;
        padding: 10px;
        background-color: #f9f9f9;
      }
    </style>
  </head>
  <body>
    <h1>EditContext API Demo</h1>

    <div class="editor" id="editor" contenteditable="true">
      Try editing this text...
    </div>

    <div class="status" id="status">
      <h3>Status:</h3>
      <div id="events"></div>
    </div>

    <script>
      const editor = document.getElementById('editor');
      const eventsDiv = document.getElementById('events');

      // Create EditContext
      const editContext = new EditContext({
        text: 'Try editing this text...',
        selectionStart: 0,
        selectionEnd: 0,
      });
      editor.editContext = editContext;

      // Handle keyboard events and track cursor position
      let lastCursorPos = 0;
      let isComposing = false;
      let compositionText = '';

      editor.addEventListener('keydown', (e) => {
        console.log('keydown', e);

        // 记录事件触发顺序
        const eventLog = [`keydown: ${e.key}`];

        // 只在必要时preventDefault
        let shouldPreventDefault = false;

        // Handle Ctrl+A (Select All)
        if (e.ctrlKey && e.key === 'a') {
          const fullText = editor.textContent;
          editContext.updateSelection(0, fullText.length);

          const range = document.createRange();
          range.setStart(editor, 0);
          range.setEnd(editor, 1);
          const sel = window.getSelection();
          sel.removeAllRanges();
          sel.addRange(range);
          shouldPreventDefault = true;
          eventLog.push('handled Ctrl+A');
        }
        // Handle Ctrl+X (Cut)
        else if (e.ctrlKey && e.key === 'x') {
          const selectedText = window.getSelection().toString();
          if (selectedText) {
            navigator.clipboard.writeText(selectedText).then(() => {
              const selection = window.getSelection();
              const range = selection.getRangeAt(0);
              range.deleteContents();

              const newText = editor.textContent;
              editContext.updateText(newText, 0, newText.length);
              editContext.updateSelection(range.startOffset, range.startOffset);
            });
          }
          shouldPreventDefault = true;
          eventLog.push('handled Ctrl+X');
        }
        // Handle delete/backspace keys
        else if (e.key === 'Delete' || e.key === 'Backspace') {
          const sel = window.getSelection();
          if (sel.rangeCount > 0) {
            const range = sel.getRangeAt(0);
            const cursorPos = range.startOffset;

            const deleteLength = e.key === 'Delete' ? 1 : -1;
            const deleteStart =
              cursorPos + (deleteLength > 0 ? 0 : deleteLength);
            const deleteEnd = deleteStart + Math.abs(deleteLength);

            const currentText = editor.textContent || '';
            const newText =
              currentText.substring(0, deleteStart) +
              currentText.substring(deleteEnd);

            editor.textContent = newText;
            lastCursorPos = deleteStart;

            editContext.updateText(newText, 0, newText.length);
            editContext.updateSelection(deleteStart, deleteStart);

            range.setStart(editor.childNodes[0], deleteStart);
            range.collapse(true);
            shouldPreventDefault = true;
            eventLog.push('handled Delete/Backspace');
          }
        }
        // 对于普通输入键，不阻止默认行为以确保composition事件触发
        else {
          eventLog.push('allowed default behavior');
        }

        // 更新光标位置
        const sel = window.getSelection();
        if (sel.rangeCount > 0) {
          lastCursorPos = sel.getRangeAt(0).startOffset;
          eventLog.push(`cursor position: ${lastCursorPos}`);
        }

        console.log('Keydown event handling:', eventLog.join(', '));

        if (shouldPreventDefault) {
          e.preventDefault();
        }
      });

      // Handle composition (IME input)
      let compositionStartPos = 0;

      // Track pending composition text
      let pendingText = '';

      editor.addEventListener('compositionstart', (e) => {
        console.log('compositionstart event triggered', e);
        isComposing = true;
        compositionText = '';
        pendingText = '';
        compositionStartPos = lastCursorPos;
        console.log('composition started at position:', compositionStartPos);
      });

      editor.addEventListener('compositionupdate', (e) => {
        console.log('compositionupdate', e);
        // Only update with latest composition text
        compositionText = e.data;

        const currentText = editor.textContent || '';
        const textBefore = currentText.substring(0, compositionStartPos);
        const textAfter = currentText.substring(
          compositionStartPos + pendingText.length
        );
        const newText = textBefore + compositionText + textAfter;

        editor.textContent = newText;
        pendingText = compositionText;

        editContext.updateText(newText, 0, newText.length);
        editContext.updateSelection(
          compositionStartPos + compositionText.length,
          compositionStartPos + compositionText.length
        );
      });

      editor.addEventListener('compositionend', (e) => {
        console.log('compositionend', e);
        isComposing = false;
        // Only keep final confirmed text
        const confirmedText = e.data;
        const currentText = editor.textContent || '';
        const textBefore = currentText.substring(0, compositionStartPos);
        const textAfter = currentText.substring(
          compositionStartPos + pendingText.length
        );
        const newText = textBefore + confirmedText + textAfter;

        editor.textContent = newText;
        lastCursorPos = compositionStartPos + confirmedText.length;

        editContext.updateText(newText, 0, newText.length);
        editContext.updateSelection(lastCursorPos, lastCursorPos);

        compositionText = '';
        pendingText = '';
      });

      editContext.addEventListener('textupdate', (event) => {
        logEvent('textupdate', event);

        // Skip all text updates during composition
        if (isComposing) {
          return;
        }

        const currentText = editor.textContent || '';
        const newText =
          currentText.substring(0, lastCursorPos) +
          event.text +
          currentText.substring(lastCursorPos);

        editor.textContent = newText;

        // Update cursor position
        const newCursorPos = lastCursorPos + event.text.length;
        lastCursorPos = newCursorPos;

        // Sync with EditContext
        editContext.updateText(newText, 0, newText.length);
        editContext.updateSelection(newCursorPos, newCursorPos);

        // Update DOM selection
        const range = document.createRange();
        if (editor.childNodes.length === 0) {
          editor.appendChild(document.createTextNode(''));
        }
        range.setStart(editor.childNodes[0], newCursorPos);
        range.collapse(true);
        const sel = window.getSelection();
        sel.removeAllRanges();
        sel.addRange(range);
      });

      editContext.addEventListener('textformatupdate', (event) => {
        logEvent('textformatupdate', event);
      });

      editContext.addEventListener('selectionchange', (event) => {
        logEvent('selectionchange', event);
        // Sync DOM selection with EditContext selection
        if (
          event.selectionStart !== undefined &&
          event.selectionEnd !== undefined
        ) {
          const range = document.createRange();
          const sel = window.getSelection();
          range.setStart(editor.childNodes[0], event.selectionStart);
          range.setEnd(editor.childNodes[0], event.selectionEnd);
          sel.removeAllRanges();
          sel.addRange(range);
        }
      });

      function logEvent(type, event) {
        const eventDiv = document.createElement('div');
        eventDiv.textContent = `${new Date().toLocaleTimeString()}: ${type}`;
        if (event.text) {
          eventDiv.textContent += ` (Text: "${event.text}")`;
        }
        if (event.updateRangeStart !== undefined) {
          eventDiv.textContent += ` [Range: ${event.updateRangeStart}-${event.updateRangeEnd}]`;
        }
        if (event.selectionStart !== undefined) {
          eventDiv.textContent += ` [Selection: ${event.selectionStart}-${event.selectionEnd}]`;
        }
        eventsDiv.prepend(eventDiv);

        // Keep only the last 5 events
        if (eventsDiv.children.length > 5) {
          eventsDiv.removeChild(eventsDiv.lastChild);
        }
      }

      // Fallback for browsers that don't support EditContext
      if (!window.EditContext) {
        editor.textContent = 'EditContext API is not supported in this browser';
        editor.style.color = 'red';
      }
    </script>
  </body>
</html>
